package com.jinxin.platform.cdacscontrolmgr.rpc;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.jinxin.platform.base.api.annotation.*;
import com.jinxin.platform.base.api.constant.APCOSApiConstans;
import com.jinxin.platform.base.common.pojo.InvokeCfg;
import com.jinxin.platform.base.common.pojo.JsonResult;
import com.jinxin.platform.base.common.support.BusinessDelegate;
import com.jinxin.platform.cdacscontrolmgr.bean.*;
import com.jinxin.platform.cdacscontrolmgr.bean.bo.OptionItem;
import com.jinxin.platform.cdacscontrolmgr.bean.enums.CarTypeEnum;
import com.jinxin.platform.cdacscontrolmgr.bean.enums.ColorEnum;
import com.jinxin.platform.cdacscontrolmgr.bean.enums.ErrorCodeEnum;
import com.jinxin.platform.cdacscontrolmgr.common.Constant;
import com.jinxin.platform.cdacscontrolmgr.common.SysOrgInfo;
import com.jinxin.platform.cdacscontrolmgr.common.SysUserVO;
import com.jinxin.platform.cdacscontrolmgr.mapper.*;
import com.jinxin.platform.cdacscontrolmgr.redis.CdacscontrolmgrRedisUtil;
import com.jinxin.platform.cdacscontrolmgr.task.CdacscontrolmgrQuartzScheduler;
import com.jinxin.platform.cdacscontrolmgr.task.PortraitDeleteTask;
import com.jinxin.platform.cdacscontrolmgr.utils.*;
import lombok.extern.slf4j.Slf4j;
import org.quartz.SchedulerException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import sun.misc.BASE64Encoder;

import javax.annotation.Resource;
import java.io.File;
import java.util.*;
import java.util.concurrent.TimeUnit;

import static com.alibaba.fastjson.JSON.toJSONString;

/**
 * @description: (描述...)
 * @return:
 * @author: Linn
 * @time: 2020/05/18
 */
@Component
@Slf4j
@NeedInf(moduleKey = Constant.MODULE_KEY, className = Constant.MODULE_CLASS_NAME)
public class CdacscontrolmgrRpc {
    @Autowired
    private BusinessDelegate businessDelegate;
    @Resource
    private IdWorker idWorker;
    @Autowired
    private CdacscontrolmgrRegistInfoMapper registInfoMapper;
    @Autowired
    private CdacscontrolmgrProjectAccessMapper projectAccessMapper;
    @Autowired
    private CdacscontrolmgrPushInfoMapper cdacscontrolmgrPushInfoMapper;
    @Autowired
    private CdacscontrolmgrProjectMapper cdacscontrolmgrProjectMapper;
    @Autowired
    private CdacscontrolmgrOwnerMapper cdacscontrolmgrOwnerMapper;
    @Value(value = "${platform.upload}")
    private String filePath;
    @Autowired
    private CdacscontrolmgrQuartzScheduler scheduler;
    @Autowired
    private CdacscontrolmgrProjectVideoMapper cdacscontrolmgrProjectVideoMapper;
    @Autowired
    private CdacscontrolmgrEventMapper cdacscontrolmgrEventMapper;
    @Autowired
    private CdacscontrolmgrRedisUtil cdacscontrolmgrRedisUtil;

    String SERIAL_NUM = Constant.CMD_SERAL_NUM;//流水号
    String CMD_ADDR = Constant.CMD_ADDR;//地址
    String KEEP_VALUE = Constant.KEEP_VALUE;//保留
    String DEFAULT_COLOR =  Constant.HEX_LINE_ONE;

    /**
     * 空闲状态LED显示( 加载广告内容和设置时间指令)
     * @param ledContent led内容
     * @param remain 剩余车位
     * @param mac 设备mac
     * @return
     */
    //@Async("asyncServiceExecutor")
    public JsonResult showLedForFree(CdyardmgrProjectLedContent ledContent,int remain,String mac) {
        cancelTempShow(mac);
        if(ledContent == null){return JsonResult.error("LED内容未配置");}
        cancelAllAd(mac);//取消4行的广告
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("cmd",Constant.CMD_TC);
        jsonObject.put("id",Constant.DEFAULT_ID);
        jsonObject.put("subcmd",Constant.SUBCMD);
        Integer freeLine1Type = ledContent.getFreeLine1Type();
        Integer freeLine2Type = ledContent.getFreeLine2Type();
        Integer freeLine3Type = ledContent.getFreeLine3Type();
        Integer freeLine4Type = ledContent.getFreeLine4Type();
        boolean isTime1 = freeLine1Type.equals(Constant.NUM_ONE);
        boolean isTime2 = freeLine2Type.equals(Constant.NUM_ONE);
        boolean isTime3 = freeLine3Type.equals(Constant.NUM_ONE);
        boolean isTime4 = freeLine4Type.equals(Constant.NUM_ONE);
        boolean hasTime = false;
        StringBuilder timeSb = new StringBuilder(Constant.CMD_HEAD);//时间显示
        String freeLine1Color = ledContent.getFreeLine1Color();
        String freeLine2Color = ledContent.getFreeLine2Color();
        String freeLine3Color = ledContent.getFreeLine3Color();
        String freeLine4Color = ledContent.getFreeLine4Color();
        String color1 = Constant.ZERO + ColorEnum.getCarCode(freeLine1Color);
        String color2 = Constant.ZERO + ColorEnum.getCarCode(freeLine2Color);
        String color3 = Constant.ZERO + ColorEnum.getCarCode(freeLine3Color);
        String color4 = Constant.ZERO + ColorEnum.getCarCode(freeLine4Color);
        if(isTime1 || isTime2 || isTime3 || isTime4){//时间
            hasTime = true;
            // AA55 2064 00 F6 0003 010200 6A5C AF
            timeSb.append(SERIAL_NUM).append(CMD_ADDR).append(KEEP_VALUE);
            String cmdTime = Constant.CMD_TIME;
            String len = Constant.FIXED_LEN_3;//3位
            timeSb.append(cmdTime).append(KEEP_VALUE).append(len);
            String lineNum,color;
            if(isTime1){
                lineNum = Constant.HEX_LINE_ONE;
                color = color1;
            }else if(isTime2){
                lineNum = Constant.HEX_LINE_TWO;
                color = color2;
            }else if(isTime3){
                lineNum = Constant.HEX_LINE_THREE;
                color = color3;
            }else {
                lineNum = Constant.HEX_LINE_FOUR;
                color = color4;
            }
            timeSb.append(lineNum).append(color);//行号 颜色
            timeSb.append(Constant.KEEP_VALUE);//保留
            // "14", "64", "00","F6","00", "03", "01", "03","00",多2位 "00","00"
            String crc16 = Crc16Util.getCrc16(SERIAL_NUM, CMD_ADDR,
                    KEEP_VALUE, cmdTime, KEEP_VALUE, len, lineNum, color, KEEP_VALUE, KEEP_VALUE, KEEP_VALUE);
            timeSb.append(crc16);//校验码
            timeSb.append(Constant.CMD_END);//结束
        }
        if(hasTime){
            byte[] bytes = NumConvertUtil.hexStringToByteArray(timeSb.toString());
            String data = StringUtil.getBase64String(bytes);
            ttransmission(bytes.length, data, mac);
            sleep200ms();
        }
        //文字显示
        StringBuilder mutilSb = new StringBuilder(Constant.CMD_HEAD);//多行临显
        StringBuilder mutilDataSb = new StringBuilder();
        mutilSb.append(SERIAL_NUM).append(CMD_ADDR).append(KEEP_VALUE);
        List<String> crcList = new ArrayList<>();//计算crc16的字符list
        crcList.add(SERIAL_NUM);
        crcList.add(CMD_ADDR);
        crcList.add(KEEP_VALUE);
        crcList.add(Constant.CMD_SHOW4);//多行临显
        mutilSb.append(Constant.CMD_SHOW4);//命令0x29
        String ywShowText = Constant.FREE_CAR_MSG + remain;//不超过60字节
        String ywText = StringUtil.convertStr2Hex(ywShowText);
        int ywLenth = StringUtil.getBytesLength(ywShowText);
        String ywLenHex = NumConvertUtil.intToHexString(ywLenth,Constant.NUM_TWO);//1字节
        int dataLength = 0;
        String freeLine1Content = ledContent.getFreeLine1Content();
        String freeLine2Content = ledContent.getFreeLine2Content();
        String freeLine3Content = ledContent.getFreeLine3Content();
        String freeLine4Content = ledContent.getFreeLine4Content();

        if(!isTime1){ //第1行
            if(freeLine1Type.equals(Constant.NUM_TWO)){ //文字(广告)
                // AA55 34640025 000B 010200 BBB6D3ADB9E2C1D9 97B9 AF
                StringBuilder azSb = new StringBuilder(Constant.CMD_HEAD);//广告显示
                List<String> crc1List = new ArrayList<>();//计算crc16的字符list
                azSb.append(SERIAL_NUM).append(CMD_ADDR).append(KEEP_VALUE).append(Constant.CMD_SHOW_AD);
                crc1List.add(SERIAL_NUM);
                crc1List.add(CMD_ADDR);
                crc1List.add(KEEP_VALUE);
                crc1List.add(Constant.CMD_SHOW_AD);
                int bytesLen1 = StringUtil.getBytesLength(freeLine1Content);
                // 长度=数据长度+3字节(行号+颜色+保留)
                String len = NumConvertUtil.intToHexString(bytesLen1+Constant.NUM_THREE, Constant.NUM_TWO);
                azSb.append(KEEP_VALUE);//保留00
                azSb.append(len);//长度2字节 因为是3~63没有超过255 这样分开是为了计算crc16 (000B)
                crc1List.add(KEEP_VALUE);
                crc1List.add(len);
                azSb.append(Constant.HEX_LINE_ONE).append(color1).append(KEEP_VALUE);//01 02 00
                crc1List.add(Constant.HEX_LINE_ONE);
                crc1List.add(color1);
                crc1List.add(KEEP_VALUE);
                String lineContent = StringUtil.convertStr2Hex(freeLine1Content);
                azSb.append(lineContent);//数据
                for (int i = 0; i < lineContent.length(); i+=Constant.NUM_TWO) {
                    crc1List.add(lineContent.substring(i,i+Constant.NUM_TWO));
                }
                crc1List.add(KEEP_VALUE);
                crc1List.add(KEEP_VALUE);
                azSb.append(Crc16Util.getCrc16(crc1List));//校验码
                azSb.append(Constant.CMD_END);//结束
                byte[] bytes = NumConvertUtil.hexStringToByteArray(azSb.toString());
                String data = StringUtil.getBase64String(bytes);
                ttransmission(bytes.length, data, mac);
                //第1行不是余位(广告)也要有缺省的时长颜色长度 000100
                dataLength = setDefaultData(mutilDataSb, crcList, dataLength);
                //间隔50ms
                sleep200ms();
            }else { //余位 (多行临显)
                String color = Constant.ZERO + ColorEnum.getCarCode(freeLine1Color);
                mutilDataSb.append(KEEP_VALUE);//第一行的时长 00长期显示 1字节
                crcList.add(KEEP_VALUE);
                mutilDataSb.append(color);//第一行的颜色 1字节
                crcList.add(color);
                mutilDataSb.append(ywLenHex);//第一行长度 1字节
                dataLength += ywLenth + Constant.NUM_THREE;  //第一行长度+3字节(时长+颜色+长度)
                crcList.add(ywLenHex);
                mutilDataSb.append(ywText);
                for (int i = 0; i < ywText.length(); i+=Constant.NUM_TWO) {
                    crcList.add(ywText.substring(i,i+Constant.NUM_TWO));
                }
            }
        }else {
            //第1行不是余位(广告)也要有缺省的时长颜色长度 000100
            dataLength = setDefaultData(mutilDataSb, crcList, dataLength);
        }
        if(!isTime2){ //第2行
            if(freeLine2Type.equals(Constant.NUM_TWO)){ //文字(广告)
                StringBuilder azSb = new StringBuilder(Constant.CMD_HEAD);//广告显示
                List<String> crc1List = new ArrayList<>();//计算crc16的字符list
                azSb.append(SERIAL_NUM).append(CMD_ADDR).append(KEEP_VALUE).append(Constant.CMD_SHOW_AD);
                crc1List.add(SERIAL_NUM);
                crc1List.add(CMD_ADDR);
                crc1List.add(KEEP_VALUE);
                crc1List.add(Constant.CMD_SHOW_AD);
                int bytesLen1 = StringUtil.getBytesLength(freeLine2Content);
                // 长度=数据长度+3字节(行号+颜色+保留)
                String len = NumConvertUtil.intToHexString(bytesLen1+Constant.NUM_THREE, Constant.NUM_TWO);
                azSb.append(KEEP_VALUE);//保留00
                azSb.append(len);//长度2字节 因为是3~63没有超过255 这样分开是为了计算crc16 (000B)
                crc1List.add(KEEP_VALUE);
                crc1List.add(len);
                azSb.append(Constant.HEX_LINE_TWO).append(color2).append(KEEP_VALUE);//01 02 00
                crc1List.add(Constant.HEX_LINE_TWO);
                crc1List.add(color2);
                crc1List.add(KEEP_VALUE);
                String lineContent = StringUtil.convertStr2Hex(freeLine2Content);
                azSb.append(lineContent);//数据
                for (int i = 0; i < lineContent.length(); i+=Constant.NUM_TWO) {
                    crc1List.add(lineContent.substring(i,i+Constant.NUM_TWO));
                }
                crc1List.add(KEEP_VALUE);
                crc1List.add(KEEP_VALUE);
                azSb.append(Crc16Util.getCrc16(crc1List));//校验码
                azSb.append(Constant.CMD_END);//结束
                byte[] bytes = NumConvertUtil.hexStringToByteArray(azSb.toString());
                String data = StringUtil.getBase64String(bytes);
                ttransmission(bytes.length, data, mac);
                //第2行不是余位(广告)也要有缺省的时长颜色长度 000100
                dataLength = setDefaultData(mutilDataSb, crcList, dataLength);
                //间隔50ms
                sleep200ms();
            }else { //余位 (多行临显)
                String color = Constant.ZERO + ColorEnum.getCarCode(freeLine2Color);
                mutilDataSb.append(KEEP_VALUE);//第2行的时长 00长期显示 1字节
                crcList.add(KEEP_VALUE);
                mutilDataSb.append(color);//第2行的颜色 1字节
                crcList.add(color);
                mutilDataSb.append(ywLenHex);//第2行长度 1字节
                dataLength += ywLenth + Constant.NUM_THREE;  //第一行长度+3字节(时长+颜色+长度)
                crcList.add(ywLenHex);
                mutilDataSb.append(ywText);
                for (int i = 0; i < ywText.length(); i+=Constant.NUM_TWO) {
                    crcList.add(ywText.substring(i,i+Constant.NUM_TWO));
                }
            }
        }else {
            //第2行不是余位(广告)也要有缺省的时长颜色长度 000100
            dataLength = setDefaultData(mutilDataSb, crcList, dataLength);
        }
        if(!isTime3){ //第3行
            if(freeLine3Type.equals(Constant.NUM_TWO)){ //文字(广告)
                StringBuilder azSb = new StringBuilder(Constant.CMD_HEAD);//广告显示
                List<String> crc1List = new ArrayList<>();//计算crc16的字符list
                azSb.append(SERIAL_NUM).append(CMD_ADDR).append(KEEP_VALUE).append(Constant.CMD_SHOW_AD);
                crc1List.add(SERIAL_NUM);
                crc1List.add(CMD_ADDR);
                crc1List.add(KEEP_VALUE);
                crc1List.add(Constant.CMD_SHOW_AD);
                int bytesLen1 = StringUtil.getBytesLength(freeLine3Content);
                // 长度=数据长度+3字节(行号+颜色+保留)
                String len = NumConvertUtil.intToHexString(bytesLen1+Constant.NUM_THREE, Constant.NUM_TWO);
                azSb.append(KEEP_VALUE);//保留00
                azSb.append(len);//长度2字节 因为是3~63没有超过255 这样分开是为了计算crc16 (000B)
                crc1List.add(KEEP_VALUE);
                crc1List.add(len);
                azSb.append(Constant.HEX_LINE_THREE).append(color3).append(KEEP_VALUE);//01 02 00
                crc1List.add(Constant.HEX_LINE_THREE);
                crc1List.add(color3);
                crc1List.add(KEEP_VALUE);
                String lineContent = StringUtil.convertStr2Hex(freeLine3Content);
                azSb.append(lineContent);//数据
                for (int i = 0; i < lineContent.length(); i+=Constant.NUM_TWO) {
                    crc1List.add(lineContent.substring(i,i+Constant.NUM_TWO));
                }
                crc1List.add(KEEP_VALUE);
                crc1List.add(KEEP_VALUE);
                azSb.append(Crc16Util.getCrc16(crc1List));//校验码
                azSb.append(Constant.CMD_END);//结束
                byte[] bytes = NumConvertUtil.hexStringToByteArray(azSb.toString());
                String data = StringUtil.getBase64String(bytes);
                ttransmission(bytes.length, data, mac);
                //第3行不是余位(广告)也要有缺省的时长颜色长度 000100
                dataLength = setDefaultData(mutilDataSb, crcList, dataLength);
                //间隔50ms
                sleep200ms();
            }else { //余位 (多行临显)
                String color = Constant.ZERO + ColorEnum.getCarCode(freeLine3Color);
                mutilDataSb.append(KEEP_VALUE);//第2行的时长 00长期显示 1字节
                crcList.add(KEEP_VALUE);
                mutilDataSb.append(color);//第3行的颜色 1字节
                crcList.add(color);
                mutilDataSb.append(ywLenHex);//第3行长度 1字节
                dataLength += ywLenth + Constant.NUM_THREE;  //第一行长度+3字节(时长+颜色+长度)
                crcList.add(ywLenHex);
                mutilDataSb.append(ywText);
                for (int i = 0; i < ywText.length(); i+=Constant.NUM_TWO) {
                    crcList.add(ywText.substring(i,i+Constant.NUM_TWO));
                }
            }
        }else {
            //第3行不是余位(广告)也要有缺省的时长颜色长度 000100
            dataLength = setDefaultData(mutilDataSb, crcList, dataLength);
        }
        if(!isTime4){ //第4行
            if(freeLine4Type.equals(Constant.NUM_TWO)){ //文字(广告)
                StringBuilder azSb = new StringBuilder(Constant.CMD_HEAD);//广告显示
                List<String> crc1List = new ArrayList<>();//计算crc16的字符list
                azSb.append(SERIAL_NUM).append(CMD_ADDR).append(KEEP_VALUE).append(Constant.CMD_SHOW_AD);
                crc1List.add(SERIAL_NUM);
                crc1List.add(CMD_ADDR);
                crc1List.add(KEEP_VALUE);
                crc1List.add(Constant.CMD_SHOW_AD);
                int bytesLen1 = StringUtil.getBytesLength(freeLine4Content);
                // 长度=数据长度+3字节(行号+颜色+保留)
                String len = NumConvertUtil.intToHexString(bytesLen1+Constant.NUM_THREE, Constant.NUM_TWO);
                azSb.append(KEEP_VALUE);//保留00
                azSb.append(len);//长度2字节 因为是3~63没有超过255 这样分开是为了计算crc16 (000B)
                crc1List.add(KEEP_VALUE);
                crc1List.add(len);
                azSb.append(Constant.HEX_LINE_FOUR).append(color4).append(KEEP_VALUE);//01 02 00
                crc1List.add(Constant.HEX_LINE_FOUR);
                crc1List.add(color4);
                crc1List.add(KEEP_VALUE);
                String lineContent = StringUtil.convertStr2Hex(freeLine4Content);
                azSb.append(lineContent);//数据
                for (int i = 0; i < lineContent.length(); i+=Constant.NUM_TWO) {
                    crc1List.add(lineContent.substring(i,i+Constant.NUM_TWO));
                }
                crc1List.add(KEEP_VALUE);
                crc1List.add(KEEP_VALUE);
                azSb.append(Crc16Util.getCrc16(crc1List));//校验码
                azSb.append(Constant.CMD_END);//结束
                byte[] bytes = NumConvertUtil.hexStringToByteArray(azSb.toString());
                String data = StringUtil.getBase64String(bytes);
                ttransmission(bytes.length, data, mac);
                //第4行不是余位(广告)也要有缺省的时长颜色长度 000100
                dataLength = setDefaultData(mutilDataSb, crcList, dataLength);
                //间隔50ms
                sleep200ms();
            }else { //余位 (多行临显)
                String color = Constant.ZERO + ColorEnum.getCarCode(freeLine4Color);
                mutilDataSb.append(KEEP_VALUE);//第2行的时长 00长期显示 1字节
                crcList.add(KEEP_VALUE);
                mutilDataSb.append(color);//第2行的颜色 1字节
                crcList.add(color);
                mutilDataSb.append(ywLenHex);//第2行长度 1字节
                dataLength += ywLenth + Constant.NUM_THREE;  //第一行长度+3字节(时长+颜色+长度)
                crcList.add(ywLenHex);
                mutilDataSb.append(ywText);
                for (int i = 0; i < ywText.length(); i+=Constant.NUM_TWO) {
                    crcList.add(ywText.substring(i,i+Constant.NUM_TWO));
                }
            }
        }else {
            //第4行不是余位(广告)也要有缺省的时长颜色长度 000100
            dataLength = setDefaultData(mutilDataSb, crcList, dataLength);
        }
        String dataLenHex = NumConvertUtil.intToHexString(dataLength, Constant.NUM_FOUR);
        mutilSb.append(dataLenHex);//长度2字节
        mutilSb.append(mutilDataSb.toString());//多行内容
        crcList.add(Constant.NUM_FOUR,dataLenHex.substring(Constant.NUM_ZERO,Constant.NUM_TWO));//长度第1个字节
        crcList.add(Constant.NUM_FIVE,dataLenHex.substring(Constant.NUM_TWO,Constant.NUM_FOUR));//长度第2个字节
        crcList.add(KEEP_VALUE);
        crcList.add(KEEP_VALUE);//加2个00
        mutilSb.append(Crc16Util.getCrc16(crcList));//校验 2字节
        mutilSb.append(Constant.CMD_END);//结束符 固定 0xAF
        byte[] dataByte = NumConvertUtil.hexStringToByteArray(mutilSb.toString());
        String dataHex = StringUtil.base64ForHexStr(mutilSb.toString());
        jsonObject.put("datalen",dataByte.length);
        jsonObject.put("data", dataHex);
        jsonObject.put("comm",Constant.COMM);
        log.info("透传下发: " + jsonObject.toJSONString());
        ttransmission(dataByte.length,dataHex,mac);
        return JsonResult.ok("success",jsonObject.toJSONString());
    }

    /**
     * 忙碌状态LED显示(加载临显和设置时间指令)
     * @param ledContent led内容
     * @param remain 剩余车位
     * @param parkTime 停车时长 分钟
     * @param amount 收费金额 元
     * @param isComein 是否是进入
     * @param carType 车辆类型
     * @param carNum 车牌号
     * @param mac 设备mac
     * @param projectId 项目ID
     * @param carMac 车牌识别道闸mac
     * @return
     */
    public JsonResult showLedForBusy(CdyardmgrProjectLedContent ledContent,int remain,String parkTime,String amount,
                                     boolean isComein,String carType, String carNum,String mac,String projectId,String carMac) {
        //先取消4行的临显
        cancelTempShow(mac);
        if(ledContent == null){return JsonResult.error("LED内容未配置");}
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("cmd",Constant.CMD_TC);
        jsonObject.put("id",Constant.DEFAULT_ID);
        jsonObject.put("subcmd",Constant.SUBCMD);
        String ywShowText = Constant.FREE_CAR_MSG + remain;//不超过60字节
        if(isComein){ //进
            Integer yjfBusyLine1Type = ledContent.getYjfBusyLine1Type();
            String yjfBusyLine1Color = ledContent.getYjfBusyLine1Color();
            String yjfBusyLine1Content = ledContent.getYjfBusyLine1Content();
            Integer yjfBusyLine2Type = ledContent.getYjfBusyLine2Type();
            String yjfBusyLine2Color = ledContent.getYjfBusyLine2Color();
            String yjfBusyLine2Content = ledContent.getYjfBusyLine2Content();
            Integer yjfBusyLine3Type = ledContent.getYjfBusyLine3Type();
            String yjfBusyLine3Color = ledContent.getYjfBusyLine3Color();
            String yjfBusyLine3Content = ledContent.getYjfBusyLine3Content();
            Integer yjfBusyLine4Type = ledContent.getYjfBusyLine4Type();
            String yjfBusyLine4Color = ledContent.getYjfBusyLine4Color();
            String yjfBusyLine4Content = ledContent.getYjfBusyLine4Content();
            boolean isTime1 = yjfBusyLine1Type.equals(Constant.NUM_ONE);
            boolean isTime2 = yjfBusyLine2Type.equals(Constant.NUM_ONE);
            boolean isTime3 = yjfBusyLine3Type.equals(Constant.NUM_ONE);
            boolean isTime4 = yjfBusyLine4Type.equals(Constant.NUM_ONE);
            StringBuilder timeSb = new StringBuilder(Constant.CMD_HEAD);//时间显示
            String color1 = Constant.ZERO + ColorEnum.getCarCode(yjfBusyLine1Color);
            String color2 = Constant.ZERO + ColorEnum.getCarCode(yjfBusyLine2Color);
            String color3 = Constant.ZERO + ColorEnum.getCarCode(yjfBusyLine3Color);
            String color4 = Constant.ZERO + ColorEnum.getCarCode(yjfBusyLine4Color);
            if(isTime1 || isTime2 || isTime3 || isTime4){//时间
                // AA55 2064 00 F6 0003 010200 6A5C AF
                timeSb.append(SERIAL_NUM).append(CMD_ADDR).append(KEEP_VALUE);
                String cmdTime = Constant.CMD_TIME;
                String len = Constant.FIXED_LEN_3;//3位
                timeSb.append(cmdTime).append(KEEP_VALUE).append(len);
                String lineNum,color;
                if(isTime1){
                    lineNum = Constant.HEX_LINE_ONE;
                    color = color1;
                }else if(isTime2){
                    lineNum = Constant.HEX_LINE_TWO;
                    color = color2;
                }else if(isTime3){
                    lineNum = Constant.HEX_LINE_THREE;
                    color = color3;
                }else {
                    lineNum = Constant.HEX_LINE_FOUR;
                    color = color4;
                }
                timeSb.append(lineNum).append(color);//行号 颜色
                timeSb.append(Constant.KEEP_VALUE);//保留
                // "14", "64", "00","F6","00", "03", "01", "03","00",多2位 "00","00"
                String crc16 = Crc16Util.getCrc16(SERIAL_NUM, CMD_ADDR,
                        KEEP_VALUE, cmdTime, KEEP_VALUE, len, lineNum, color, KEEP_VALUE, KEEP_VALUE, KEEP_VALUE);
                timeSb.append(crc16);//校验码
                timeSb.append(Constant.CMD_END);//结束
                cancelAd(lineNum,mac);//取消该行的广告
                byte[] bytes = NumConvertUtil.hexStringToByteArray(timeSb.toString());
                String data = StringUtil.getBase64String(bytes);
                ttransmission(bytes.length, data, mac);
                sleep200ms();
            }
            //文字显示
            StringBuilder mutilSb = new StringBuilder(Constant.CMD_HEAD);//多行临显
            StringBuilder mutilDataSb = new StringBuilder();
            mutilSb.append(SERIAL_NUM).append(CMD_ADDR).append(KEEP_VALUE);
            List<String> crcList = new ArrayList<>();//计算crc16的字符list
            crcList.add(SERIAL_NUM);
            crcList.add(CMD_ADDR);
            crcList.add(KEEP_VALUE);
            crcList.add(Constant.CMD_SHOW4);//多行临显
            mutilSb.append(Constant.CMD_SHOW4);//命令0x29
            int dataLength = 0;
            dataLength = setLineData(carType, carNum, yjfBusyLine1Type, yjfBusyLine1Content,
                    isTime1, color1, mutilDataSb, crcList, ywShowText, dataLength,parkTime,amount,true,0);
            dataLength = setLineData(carType, carNum, yjfBusyLine2Type, yjfBusyLine2Content,
                    isTime2, color2, mutilDataSb, crcList, ywShowText, dataLength,parkTime,amount,true,0);
            dataLength = setLineData(carType, carNum, yjfBusyLine3Type, yjfBusyLine3Content,
                    isTime3, color3, mutilDataSb, crcList, ywShowText, dataLength,parkTime,amount,true,0);
            dataLength = setLineData(carType, carNum, yjfBusyLine4Type, yjfBusyLine4Content,
                    isTime4, color4, mutilDataSb, crcList, ywShowText, dataLength,parkTime,amount,true,0);

            String dataLenHex = NumConvertUtil.intToHexString(dataLength, Constant.NUM_FOUR);
            mutilSb.append(dataLenHex);//长度2字节
            mutilSb.append(mutilDataSb.toString());//多行内容
            crcList.add(Constant.NUM_FOUR,dataLenHex.substring(Constant.NUM_ZERO,Constant.NUM_TWO));//长度第1个字节
            crcList.add(Constant.NUM_FIVE,dataLenHex.substring(Constant.NUM_TWO,Constant.NUM_FOUR));//长度第2个字节
            crcList.add(KEEP_VALUE);
            crcList.add(KEEP_VALUE);//加2个00
            mutilSb.append(Crc16Util.getCrc16(crcList));//校验 2字节
            mutilSb.append(Constant.CMD_END);//结束符 固定 0xAF
            byte[] dataByte = NumConvertUtil.hexStringToByteArray(mutilSb.toString());
            String dataHex = StringUtil.base64ForHexStr(mutilSb.toString());
            jsonObject.put("datalen",dataByte.length);
            jsonObject.put("data", dataHex);
            jsonObject.put("comm",Constant.COMM);
            log.info("进口忙碌LED透传下发: "+jsonObject.toJSONString());
            ttransmission(dataByte.length,dataHex,mac);
            sleep200ms();
            //语音播报 1不播报2结束语3结束语+车牌号4结束语+车辆类型+车牌号
            Integer yjfBroadcastType = ledContent.getYjfBroadcastType();
            String voiceContent = ledContent.getYjfBroadcastContent();
            String welcomeHex = StringUtils.isEmpty(voiceContent) ? Constant.WELCOME_HEX : voiceContent.replace(",","");
            if(yjfBroadcastType != null && yjfBroadcastType != Constant.NUM_ONE){
                String contentHex;
                String carNumHex = StringUtil.convertStr2Hex(carNum);
                switch (yjfBroadcastType){
                    case 3:
                        contentHex = StringUtil.convertStr2Hex(carNum)+welcomeHex;
                        break;
                    case 4:
                        contentHex = StringUtil.convertStr2Hex(carNum)+ CarTypeEnum.getCarTypeCode(carType)+welcomeHex;
                        break;
                    default:
                        contentHex = welcomeHex;
                        break;
                }
                double parkFee = Double.parseDouble(amount);
                log.info("进口播放语音");
                if(parkFee > 0){//车牌号+请缴费
                    playVoice(carNumHex+Constant.PAY_HEX,mac);
                }else {
                    if(Constant.THE_CAR_NO_RIGHT.equals(carType)){
                        playVoice(carNumHex + CarTypeEnum.getCarTypeCode(carType),mac);
                    }else {
                        playVoice(contentHex,mac);
                    }
                }
            }
        }else { //出
            Integer xjfBusyLine1Type = ledContent.getXjfBusyLine1Type();
            String xjfBusyLine1Color = ledContent.getXjfBusyLine1Color();
            String xjfBusyLine1Content = ledContent.getXjfBusyLine1Content();
            Integer xjfBusyLine2Type = ledContent.getXjfBusyLine2Type();
            String xjfBusyLine2Color = ledContent.getXjfBusyLine2Color();
            String xjfBusyLine2Content = ledContent.getXjfBusyLine2Content();
            Integer xjfBusyLine3Type = ledContent.getXjfBusyLine3Type();
            String xjfBusyLine3Color = ledContent.getXjfBusyLine3Color();
            String xjfBusyLine3Content = ledContent.getXjfBusyLine3Content();
            Integer xjfBusyLine4Type = ledContent.getXjfBusyLine4Type();
            String xjfBusyLine4Color = ledContent.getXjfBusyLine4Color();
            String xjfBusyLine4Content = ledContent.getXjfBusyLine4Content();
            boolean isTime1 = xjfBusyLine1Type.equals(Constant.NUM_ONE);
            boolean isTime2 = xjfBusyLine2Type.equals(Constant.NUM_ONE);
            boolean isTime3 = xjfBusyLine3Type.equals(Constant.NUM_ONE);
            boolean isTime4 = xjfBusyLine4Type.equals(Constant.NUM_ONE);
            StringBuilder timeSb = new StringBuilder(Constant.CMD_HEAD);//时间显示
            String color1 = Constant.ZERO + ColorEnum.getCarCode(xjfBusyLine1Color);
            String color2 = Constant.ZERO + ColorEnum.getCarCode(xjfBusyLine2Color);
            String color3 = Constant.ZERO + ColorEnum.getCarCode(xjfBusyLine3Color);
            String color4 = Constant.ZERO + ColorEnum.getCarCode(xjfBusyLine4Color);
            if(isTime1 || isTime2 || isTime3 || isTime4){//时间
                // AA55 2064 00 F6 0003 010200 6A5C AF
                timeSb.append(SERIAL_NUM).append(CMD_ADDR).append(KEEP_VALUE);
                String cmdTime = Constant.CMD_TIME;
                String len = Constant.FIXED_LEN_3;//3位
                timeSb.append(cmdTime).append(KEEP_VALUE).append(len);
                String lineNum,color;
                if(isTime1){
                    lineNum = Constant.HEX_LINE_ONE;
                    color = color1;
                }else if(isTime2){
                    lineNum = Constant.HEX_LINE_TWO;
                    color = color2;
                }else if(isTime3){
                    lineNum = Constant.HEX_LINE_THREE;
                    color = color3;
                }else {
                    lineNum = Constant.HEX_LINE_FOUR;
                    color = color4;
                }
                timeSb.append(lineNum).append(color);//行号 颜色
                timeSb.append(Constant.KEEP_VALUE);//保留
                // "14", "64", "00","F6","00", "03", "01", "03","00",多2位 "00","00"
                String crc16 = Crc16Util.getCrc16(SERIAL_NUM, CMD_ADDR,
                        KEEP_VALUE, cmdTime, KEEP_VALUE, len, lineNum, color, KEEP_VALUE, KEEP_VALUE, KEEP_VALUE);
                timeSb.append(crc16);//校验码
                timeSb.append(Constant.CMD_END);//结束
                cancelAd(lineNum,mac);//取消该行的广告
                byte[] bytes = NumConvertUtil.hexStringToByteArray(timeSb.toString());
                String data = StringUtil.getBase64String(bytes);
                ttransmission(bytes.length, data, mac);
                sleep200ms();
            }
            //文字显示
            StringBuilder mutilSb = new StringBuilder(Constant.CMD_HEAD);//多行临显
            StringBuilder mutilDataSb = new StringBuilder();
            mutilSb.append(SERIAL_NUM).append(CMD_ADDR).append(KEEP_VALUE);
            List<String> crcList = new ArrayList<>();//计算crc16的字符list
            crcList.add(SERIAL_NUM);
            crcList.add(CMD_ADDR);
            crcList.add(KEEP_VALUE);
            crcList.add(Constant.CMD_SHOW4);//多行临显
            mutilSb.append(Constant.CMD_SHOW4);//命令0x29
            int dataLength = 0;
            Integer showDuration = ledContent.getShowDuration();
            dataLength = setLineData(carType, carNum, xjfBusyLine1Type, xjfBusyLine1Content,
                    isTime1, color1, mutilDataSb, crcList, ywShowText, dataLength,parkTime,amount,false,showDuration);
            dataLength = setLineData(carType, carNum, xjfBusyLine2Type, xjfBusyLine2Content,
                    isTime2, color2, mutilDataSb, crcList, ywShowText, dataLength,parkTime,amount,false,showDuration);
            dataLength = setLineData(carType, carNum, xjfBusyLine3Type, xjfBusyLine3Content,
                    isTime3, color3, mutilDataSb, crcList, ywShowText, dataLength,parkTime,amount,false,showDuration);
            dataLength = setLineData(carType, carNum, xjfBusyLine4Type, xjfBusyLine4Content,
                    isTime4, color4, mutilDataSb, crcList, ywShowText, dataLength,parkTime,amount,false,showDuration);

            String dataLenHex = NumConvertUtil.intToHexString(dataLength, Constant.NUM_FOUR);
            mutilSb.append(dataLenHex);//长度2字节
            mutilSb.append(mutilDataSb.toString());//多行内容
            crcList.add(Constant.NUM_FOUR,dataLenHex.substring(Constant.NUM_ZERO,Constant.NUM_TWO));//长度第1个字节
            crcList.add(Constant.NUM_FIVE,dataLenHex.substring(Constant.NUM_TWO,Constant.NUM_FOUR));//长度第2个字节
            crcList.add(KEEP_VALUE);
            crcList.add(KEEP_VALUE);//加2个00
            mutilSb.append(Crc16Util.getCrc16(crcList));//校验 2字节
            mutilSb.append(Constant.CMD_END);//结束符 固定 0xAF
            byte[] dataByte = NumConvertUtil.hexStringToByteArray(mutilSb.toString());
            String dataHex = StringUtil.base64ForHexStr(mutilSb.toString());
            jsonObject.put("datalen",dataByte.length);
            jsonObject.put("data", dataHex);
            jsonObject.put("comm",Constant.COMM);
            log.info("出口忙碌LED透传下发: "+jsonObject.toJSONString());
            ttransmission(dataByte.length,dataHex,mac);
            sleep200ms();
            //语音播报 1不播报2结束语3结束语+车牌号4结束语+车辆类型+车牌号
            Integer xjfBroadcastType = ledContent.getXjfBroadcastType();
            String voiceContent = ledContent.getXjfBroadcastContent();
            String welcomeHex = StringUtils.isEmpty(voiceContent) ? Constant.WELCOME_HEX : voiceContent.replace(",","");
            if(xjfBroadcastType != null && xjfBroadcastType != Constant.NUM_ONE){
                String contentHex;
                String carNumHex = StringUtil.convertStr2Hex(carNum);
                switch (xjfBroadcastType){
                    case 3:
                        contentHex = welcomeHex+StringUtil.convertStr2Hex(carNum);
                        break;
                    case 4:
                        contentHex = welcomeHex+StringUtil.convertStr2Hex(carNum)+ CarTypeEnum.getCarTypeCode(carType);
                        break;
                    default:
                        contentHex = welcomeHex;
                        break;
                }
                double parkFee = Double.parseDouble(amount);
                log.info("出口播放语音");
                if(parkFee > 0){//需要缴费的播放:车牌号+请缴费
                    playVoice(carNumHex+Constant.PAY_HEX,mac);
                }else {
                    if(Constant.THE_CAR_NO_RIGHT.equals(carType)){//此车无权限
                        playVoice(carNumHex + CarTypeEnum.getCarTypeCode(carType),mac);
                    }else {
                        playVoice(contentHex,mac);
                    }
                }
                sleep200ms();
            }
            //二维码显示
            Integer showPaymentCode = ledContent.getShowPaymentCode();
            if(showPaymentCode != null && showPaymentCode == Constant.NUM_ONE){
                String domain = getDomain();
                boolean isHttps = !StringUtil.isEmpty(domain) && domain.startsWith(Constant.HTTPS_SCHEME);
                String url = domain + Constant.QECODE_PAY_URL + "?carNum="+carNum+"&proId="+projectId+"&mac="+carMac;
                String qrCodeUrl = null;
                try {
                    HttpClientResult httpClientResult = HttpClientPoolUtil.doGet(url, isHttps);
                    String content = httpClientResult.getContent();
                    if(content != null){
                        JsonResult jsonResult1 = JSONObject.parseObject(content, JsonResult.class);
                        if(jsonResult1.getSuccess() && jsonResult1.getData() != null){
                            JSONObject jsonObj = JSONObject.parseObject(jsonResult1.getData().toString());
                            qrCodeUrl = jsonObj.getString("addr");
                        }
                    }
                } catch (Exception e) {
                    log.error("获取付款二维码出错: " + e.toString());
                }
                if(!StringUtil.isEmpty(qrCodeUrl)){
                    showQrcode(qrCodeUrl,mac);
                }
            }
        }
        return JsonResult.ok("success",jsonObject.toJSONString());
    }

    /**
     *
     * @param lineOne 第一行显示文字
     * @param remain 剩余车位(第二行)
     * @param mac 设备mac
     */
    public void showYwp(String lineOne,int remain,String mac){
        cancelTempShowForYwp(mac);
        cancelAdForYwp(mac);
        int bytesLen1 = StringUtil.getBytesLength(lineOne);
        int bytesLen2 = StringUtil.getBytesLength(remain+"");
        String len1 = NumConvertUtil.intToHexString(bytesLen1, Constant.NUM_TWO);//第一行长度
        String len2 = NumConvertUtil.intToHexString(bytesLen2, Constant.NUM_TWO);//第二行长度
        String preHex = Constant.KEEP_VALUE + Constant.HEX_LINE_ONE;//时长+颜色0001
        String line1Hex = preHex+len1+StringUtil.convertStr2Hex(lineOne);
        String line2Hex = preHex+len2+StringUtil.convertStr2Hex(remain+"");
        int totalLen = bytesLen1 + Constant.NUM_THREE +bytesLen2 + Constant.NUM_THREE;
        String lenHex = NumConvertUtil.intToHexString(totalLen, Constant.NUM_TWO);
        JSONObject jsonObj = new JSONObject();
        jsonObj.put("p1",Constant.CMD_ADDR);//地址
        jsonObj.put("p2",lenHex);//数据长度
        jsonObj.put("p3",line1Hex);//第一行1B第一行时长+1B第一行颜色+1B第一行长度+NB第一行显 示的内容  长度为0则内容为空
        jsonObj.put("p4",line2Hex);//第二行
        jsonObj.put("p5","");//第三行
        jsonObj.put("p6","");//第四行
        controlDevice(Constant.CMD_LOAD_TEMP_SHOW,mac, jsonObj.toJSONString());
    }

    //取消余位屏广告
    private void cancelAdForYwp(String mac){
        for (int i = 1; i <= 2; i++) {
            JSONObject jsonObj = new JSONObject();
            jsonObj.put("p1",Constant.CMD_ADDR);//地址
            jsonObj.put("p2",Constant.FIXED_LEN_3);//数据长度 固定是03
            jsonObj.put("p3",Constant.ZERO+i);//加载内容的行号
            jsonObj.put("p4",DEFAULT_COLOR);//加载内容的显示颜色 01单色
            jsonObj.put("p5","");//内容GB2312编码 为空
            controlDevice(Constant.CMD_LOAD_AD,mac, jsonObj.toJSONString());
            sleep200ms();
        }
    }

    //取消余位屏临显
    private void cancelTempShowForYwp(String mac){
        for (int i = 1; i <= 2; i++) {
            JSONObject jsonObj = new JSONObject();
            jsonObj.put("p1",Constant.CMD_ADDR);//地址
            jsonObj.put("p2",Constant.FIXED_LEN_3);//数据长度 固定是03
            jsonObj.put("p3",Constant.ZERO+i);//加载内容的行号
            jsonObj.put("p4",DEFAULT_COLOR);//加载内容的显示颜色 01单色
            jsonObj.put("p5","");//内容GB2312编码 为空
            controlDevice(Constant.CMD_CANCEL_TEMP_SHOW,mac, jsonObj.toJSONString());
            sleep200ms();
        }
    }

    private int setLineData(String carType, String carNum, Integer yjfBusyLineType, String yjfBusyLine3Content,
                            boolean isTime, String color3, StringBuilder mutilDataSb, List<String> crcList,
                            String ywShowText, int dataLength,String parkTime,String amount,boolean isComein,int showTime) {
        if(!isTime){ //第3行
            // 1时间2车辆类型3车牌号4余位(进)或停车时长(出)5NO或金额6文字
            String lineText = "";
            switch (yjfBusyLineType){
                case 2:lineText = carType;break;
                case 3:lineText = carNum;break;
                case 4:lineText = isComein ? ywShowText : Constant.PARK_TIME_PREX+parkTime;break;
                case 5:lineText = Constant.PARK_AMOUNT_PREX+amount+Constant.PARK_AMOUNT_SUFFIX;break;
                case 6:lineText = yjfBusyLine3Content;break;
            }
            if(StringUtils.isEmpty(lineText)){
                dataLength = setDefaultData(mutilDataSb, crcList, dataLength);
            }else {
                String showTimeHex = NumConvertUtil.intToHexString(showTime, Constant.NUM_TWO);//1字节;
                dataLength = setData(color3, mutilDataSb, crcList, dataLength, lineText,showTimeHex);
            }
        }else {
            //第一行不是余位(广告)也要有缺省的时长颜色长度 000100
            dataLength = setDefaultData(mutilDataSb, crcList, dataLength);
        }
        return dataLength;
    }
    private int setDefaultData(StringBuilder mutilDataSb, List<String> crcList, int dataLength) {
        mutilDataSb.append(KEEP_VALUE);//时长 00长期显示 1字节
        crcList.add(KEEP_VALUE);
        mutilDataSb.append(DEFAULT_COLOR);//颜色 1字节
        crcList.add(DEFAULT_COLOR);
        mutilDataSb.append(KEEP_VALUE);//长度00 1字节
        dataLength += Constant.NUM_THREE;  //3字节
        crcList.add(KEEP_VALUE);
        return dataLength;
    }

    private int setData(String color1, StringBuilder mutilDataSb, List<String> crcList,
                        int dataLength, String lineText,String showTimeHex) {
        String ywText = StringUtil.convertStr2Hex(lineText);
        int ywLenth = StringUtil.getBytesLength(lineText);
        String ywLenHex = NumConvertUtil.intToHexString(ywLenth, Constant.NUM_TWO);//1字节
        mutilDataSb.append(showTimeHex);//一行时长 00长期显示 1字节
        crcList.add(showTimeHex);
        mutilDataSb.append(color1);//一行颜色 1字节
        crcList.add(color1);
        mutilDataSb.append(ywLenHex);//一行长度 1字节
        dataLength += ywLenth + Constant.NUM_THREE;  //第一行长度+3字节(时长+颜色+长度)
        crcList.add(ywLenHex);
        mutilDataSb.append(ywText);
        for (int i = 0; i < ywText.length(); i += Constant.NUM_TWO) {
            crcList.add(ywText.substring(i, i + Constant.NUM_TWO));
        }
        return dataLength;
    }

    private void cancelAllAd(String mac){
        for (int i = 1; i <= 4; i++) {
            cancelAd(Constant.ZERO+i,mac);
            sleep200ms();
        }
    }

    /**
     * 取消广告(加载广告内容指令) AA55 16640025 0003 010100 EFF0 AF
     */
    private void cancelAd(String lineHex,String mac){
        StringBuilder sb = new StringBuilder(Constant.CMD_HEAD);
        sb.append(SERIAL_NUM).append(CMD_ADDR).append(KEEP_VALUE);
        List<String> crcList = new ArrayList<>();//计算crc16的字符list
        crcList.add(SERIAL_NUM);
        crcList.add(CMD_ADDR);
        crcList.add(KEEP_VALUE);
        String cmdShowAd = Constant.CMD_SHOW_AD;
        String len = Constant.FIXED_LEN_3;//3位
        sb.append(cmdShowAd).append(KEEP_VALUE).append(len);
        crcList.add(cmdShowAd);
        crcList.add(KEEP_VALUE);
        crcList.add(len);
        sb.append(lineHex).append(DEFAULT_COLOR).append(KEEP_VALUE);//行号 颜色 保留
        crcList.add(lineHex);
        crcList.add(DEFAULT_COLOR);
        crcList.add(KEEP_VALUE);
        crcList.add(KEEP_VALUE);
        crcList.add(KEEP_VALUE);
        sendCmd(mac, sb, crcList);
    }

    /**
     * 取消4行临显(即时显示)内容命令 AA55 1864 0021 0001 0F 6590 AF
     */
    public void cancelTempShow(String mac){
        StringBuilder sb = new StringBuilder(Constant.CMD_HEAD);
        sb.append(SERIAL_NUM).append(CMD_ADDR).append(KEEP_VALUE);
        List<String> crcList = new ArrayList<>();//计算crc16的字符list
        crcList.add(SERIAL_NUM);
        crcList.add(CMD_ADDR);
        crcList.add(KEEP_VALUE);
        String cmdCancelTemp = Constant.CMD_CANCEL_TEMP;
        sb.append(cmdCancelTemp).append(KEEP_VALUE).append(Constant.HEX_LINE_ONE);
        crcList.add(cmdCancelTemp);
        crcList.add(KEEP_VALUE);
        crcList.add(Constant.HEX_LINE_ONE);
        sb.append(Constant.HEX_0F);//内容 4行 0000 1111
        crcList.add(Constant.HEX_0F);
        crcList.add(KEEP_VALUE);
        crcList.add(KEEP_VALUE);
        sendCmd(mac, sb, crcList);
    }

    /**
     * 二维码显示指令0x28
     * AA5521640028 0010 000001 7777772E62616964752E636F6D E41DAF
     */
    private void showQrcode(String content,String mac){
        StringBuilder sb = new StringBuilder(Constant.CMD_HEAD);
        sb.append(SERIAL_NUM).append(CMD_ADDR).append(KEEP_VALUE);
        List<String> crcList = new ArrayList<>();//计算crc16的字符list
        crcList.add(SERIAL_NUM);
        crcList.add(CMD_ADDR);
        crcList.add(KEEP_VALUE);
        String cmdShowAd = Constant.CMD_SHOW_QRCODE;
        int ywLenth = StringUtil.getBytesLength(content)+Constant.NUM_THREE;
        String lenHex = NumConvertUtil.intToHexString(ywLenth,Constant.NUM_TWO);//1字节
        sb.append(cmdShowAd).append(KEEP_VALUE).append(lenHex);
        crcList.add(cmdShowAd);
        crcList.add(KEEP_VALUE);
        crcList.add(lenHex);
        //00 二维码显示模式=0 时表示二维码显示为两行居中模式
        //00 表示二维码显示的时长，单位 秒，=0 时，长期显示。
        //01 表示二维码本身的颜色 = 1 时红色，=2 时绿色，=3 时黄色，其他无效
        sb.append(KEEP_VALUE).append(KEEP_VALUE).append(DEFAULT_COLOR);
        crcList.add(KEEP_VALUE);
        crcList.add(KEEP_VALUE);
        crcList.add(DEFAULT_COLOR);
        String lineContent = StringUtil.convertStr2Hex(content);
        sb.append(lineContent);//数据
        for (int i = 0; i < lineContent.length(); i+=Constant.NUM_TWO) {
            crcList.add(lineContent.substring(i,i+Constant.NUM_TWO));
        }
        crcList.add(KEEP_VALUE);
        crcList.add(KEEP_VALUE);
        sendCmd(mac, sb, crcList);
    }

    private void sendCmd(String mac, StringBuilder sb, List<String> crcList) {
        String crc16 = Crc16Util.getCrc16(crcList);
        sb.append(crc16);//校验码
        sb.append(Constant.CMD_END);//结束
        byte[] bytes = NumConvertUtil.hexStringToByteArray(sb.toString());
        String data = StringUtil.getBase64String(bytes);
        ttransmission(bytes.length, data, mac);
        sleep200ms();
    }

    /**
     * 语音播放指令 AA551D64 0022 0009 D4C14231323334350A 8876AF
     */
    private void playVoice(String contentHex,String mac){
        StringBuilder sb = new StringBuilder(Constant.CMD_HEAD);
        sb.append(SERIAL_NUM).append(CMD_ADDR).append(KEEP_VALUE);
        List<String> crcList = new ArrayList<>();//计算crc16的字符list
        crcList.add(SERIAL_NUM);
        crcList.add(CMD_ADDR);
        crcList.add(KEEP_VALUE);
        String cmdShowAd = Constant.CMD_PLAY_VOICE;
        byte[] bytes = NumConvertUtil.hexStringToByteArray(contentHex);
        String len = NumConvertUtil.intToHexString(bytes.length,Constant.NUM_TWO);//1~100 1字节
        sb.append(cmdShowAd).append(KEEP_VALUE).append(len);
        crcList.add(cmdShowAd);
        crcList.add(KEEP_VALUE);
        crcList.add(len);
        sb.append(contentHex);
        for (int i = 0; i < contentHex.length(); i+=Constant.NUM_TWO) {
            crcList.add(contentHex.substring(i,i+Constant.NUM_TWO));
        }
        crcList.add(KEEP_VALUE);
        crcList.add(KEEP_VALUE);
        sendCmd(mac, sb, crcList);
    }

    private void sleep200ms(){
        try {
            TimeUnit.MILLISECONDS.sleep(Constant.SLEEP_TIME);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public JsonResult ttransmission(int len,String data,String mac){
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("cmd",Constant.CMD_TC);
        jsonObject.put("id",Constant.DEFAULT_ID);
        jsonObject.put("subcmd",Constant.SUBCMD);
        jsonObject.put("datalen",len);
        jsonObject.put("data",data);
        jsonObject.put("comm",Constant.COMM);
        JSONObject jsonObj = new JSONObject();
        jsonObj.put("p1",jsonObject.toJSONString());
//        String controlParams = URLEncoder.encodeURIComponent(pJsonObj.toJSONString());
        JsonResult result = controlDevice(Constant.CMD_TC,mac, jsonObj.toJSONString());
//        return JsonResult.ok("success","test");
        log.info("门禁项目模块透传指令显示内容返回："+result.toString());
        String resultStr = result.getData().toString();
        boolean succeed = isSucceed(resultStr, Constant.DEVICE_RESPONSE_CODE, Constant.ZERO);
        if (succeed) {
            return JsonResult.ok("门禁项目模块透传指令显示内容成功",result.getData());
        }else {
            return JsonResult.error("门禁项目模块透传指令控制失败,原因是:"+resultStr);
        }
    }

    public String getDomain(){
        String key = Constant.DOMAIN_URL;
        if(cdacscontrolmgrRedisUtil.hasKey(key)){
            return cdacscontrolmgrRedisUtil.get(key);
        }
        return "";
    }
    /**
     * 向用户推送消息
     * @param userId 用户ID
     * @param orgId 组织ID
     * @param message 消息内容
     * @param time 时间
     * @param title 消息主题
     * @param source 消息来源
     * @return JsonResult
     */
    @APCOSApiOperation(value = "sendMessage", description = "向用户推送消息", infDict = "向用户推送消息",
            reqDemo = "{\n" +
                    "  \"msg_source\": \"设备模块\",\n" +
                    "  \"msg_title\": \"设备预警\",\n" +
                    "  \"msg_body\": \"不知道哪个设备坏了\",\n" +
                    "  \"userId\": \"17046c56ef8141a3beecd262bdd5b8d0\",\n" +
                    "  \"orgId\": \"123\",\n" +
                    "  \"userId\": \"123\",\n" +
                    "  \"msg_time\": \"时间\"\n" +
                    "} ", respDemo = "{}",
            infType = APCOSApiConstans.INF_TYPE_OTHER)
    @APCOSApiRequests({
            @APCOSApiRequest(name = "msg_body", value = "消息内容", dict = "消息内容",matchType = APCOSApiConstans.MATCH_TYPE_EQ,
                    fieldType = APCOSApiConstans.FIELD_TYPE_GN,required = true,
                    dataType = APCOSApiConstans.DATA_TYPE_STR,lenth = "255",remark = "消息内容"),
            @APCOSApiRequest(name = "msg_source", value = "消息来源", dict = "消息来源",matchType = APCOSApiConstans.MATCH_TYPE_EQ,
                    fieldType = APCOSApiConstans.FIELD_TYPE_GN,required = true,
                    dataType = APCOSApiConstans.DATA_TYPE_STR,lenth = "50",remark = "消息来源"),
            @APCOSApiRequest(name = "msg_time", value = "时间", dict = "时间",matchType = APCOSApiConstans.MATCH_TYPE_EQ,
                    fieldType = APCOSApiConstans.FIELD_TYPE_GN,
                    dataType = APCOSApiConstans.DATA_TYPE_STR,lenth = "50",remark = "时间"),
            @APCOSApiRequest(name = "msg_title", value = "消息主题", dict = "消息主题",matchType = APCOSApiConstans.MATCH_TYPE_EQ,
                    fieldType = APCOSApiConstans.FIELD_TYPE_GN,required = true,
                    dataType = APCOSApiConstans.DATA_TYPE_STR,lenth = "50",remark = "消息主题"),
            @APCOSApiRequest(name = "orgId", value = "组织ID", dict = "组织ID",matchType = APCOSApiConstans.MATCH_TYPE_EQ,
                    fieldType = APCOSApiConstans.FIELD_TYPE_GN,
                    dataType = APCOSApiConstans.DATA_TYPE_STR,lenth = "32",remark = "组织ID"),
            @APCOSApiRequest(name = "userId", value = "用户ID", dict = "用户ID",matchType = APCOSApiConstans.MATCH_TYPE_EQ,
                    fieldType = APCOSApiConstans.FIELD_TYPE_GN,
                    dataType = APCOSApiConstans.DATA_TYPE_STR,lenth = "32",remark = "用户ID"),
    })
    public JsonResult sendMessage(String userId,String orgId,String message,String time,String title,String source) {
        if(StringUtils.isEmpty(userId) && StringUtils.isEmpty(orgId)){
            return JsonResult.error("用户ID和组织ID不能都为空");
        }
        InvokeCfg cfg = new InvokeCfg();
        cfg.setModuleKey(Constant.MODULE_KEY);
        cfg.setClassName(Constant.MODULE_CLASS_NAME);
        cfg.setFunName("sendMessage");
        HashMap<String, String> map = new HashMap<>();
        map.put("userId",userId);
        map.put("orgId",orgId);
        map.put("msg_body",message);
        map.put("msg_time", time);
        map.put("msg_title", title);
        map.put("msg_source", source);
        cfg.setArgs(map);
        return businessDelegate.invoke(cfg);
    }

    public List<CdyardmgrPushInfo> listMsgsByUserId(String userId){
        HashMap param = new HashMap();
        param.put("userId",userId);
        param.put("startDate",DateUtil.getLast7DaysDate());//查询最近一周的
        return cdacscontrolmgrPushInfoMapper.list(param);
    }

    public List<CdyardmgrEvent> listEventByUserId(String userId){
        HashMap param = new HashMap();
        param.put("userId",userId);
        param.put("startDate",DateUtil.getLast7DaysDate());//查询最近一周的
        return cdacscontrolmgrEventMapper.list(param);
    }

    /**
     * 根据用户id获取所在组织
     *
     * @return SysUser
     */
    @APCOSApiOperation(value = "getOrgByUserId", description = "根据用户ID所在的组织", infDict = "根据用户ID所在的组织",
            reqDemo = "{\"userId\":\"123\"}", respDemo = "{\"orgId\":\"123\"}",
            infType = APCOSApiConstans.INF_TYPE_OTHER)
    @APCOSApiRequests({
            @APCOSApiRequest(name = "userId", value = "用户ID", dict = "用户ID",matchType = APCOSApiConstans.MATCH_TYPE_EQ,
                    fieldType = APCOSApiConstans.FIELD_TYPE_GN,
                    dataType = APCOSApiConstans.DATA_TYPE_STR,lenth = "50",remark = "用户ID"),
    })
    @APCOSApiResponses({
            @APCOSApiResponse(name = "orgId", value = "组织ID", dict = "组织ID",required = true,
                    fieldType = APCOSApiConstans.FIELD_TYPE_GN,
                    dataType = APCOSApiConstans.DATA_TYPE_STR,lenth = "50",remark = "组织ID"),
    })
    public SysOrgInfo getOrgByUserId(String userId) {
        InvokeCfg cfg = new InvokeCfg();
        cfg.setModuleKey(Constant.MODULE_KEY);
        cfg.setClassName(Constant.MODULE_CLASS_NAME);
        cfg.setFunName("getOrgByUserId");
        HashMap<String, String> map = new HashMap<>();
        map.put("userId",userId);
        cfg.setArgs(map);
        JsonResult result = businessDelegate.invoke(cfg);
        if (result.getSuccess()) {
            if (result.getData() == null) {
                return null;
            }
            log.info("getOrgByUserId:" + result.getData());
            return JSON.parseObject(toJSONString(result.getData()), SysOrgInfo.class);
        }
//        else {
//            SysOrgInfo userVO = new SysOrgInfo();
//            userVO.setOrgId("1");
//            userVO.setOrgName("一个牛逼的公司");
//            return userVO;
//        }
        return null;
    }

    /**
     * 查询组织下的用户
     *
     * @return SysUser
     */
    @APCOSApiOperation(value = "getUsersByOrgId", description = "按条件获取用户", infDict = "按条件获取用户",
            reqDemo = "{\"orgId\":\"123\"}", respDemo = "{\n" +
            "  \"data\": [\n" +
            "    {\n" +
            "      \"id\": \"用户ID\",\n" +
            "      \"name\": \"用户姓名\",\n" +
            "      \"userAccount\":\"登录账户\",\n" +
            "      \"userPhone\":\"用户电话\"\n" +
            "    }\n" +
            "  ]\n" +
            "} ",
            infType = APCOSApiConstans.INF_TYPE_OTHER)
    @APCOSApiRequests({
            @APCOSApiRequest(name = "orgId", value = "组织ID", dict = "组织ID",matchType = APCOSApiConstans.MATCH_TYPE_EQ,
                    fieldType = APCOSApiConstans.FIELD_TYPE_GN,
                    dataType = APCOSApiConstans.DATA_TYPE_STR,lenth = "50",remark = "组织ID"),
    })
    @APCOSApiResponses({
            @APCOSApiResponse(name = "data", value = "返回集合", dict = "返回集合",required = true,
                    fieldType = APCOSApiConstans.FIELD_TYPE_GN,keyType = APCOSApiConstans.KEY_TYPE_LIST,
                    dataType = APCOSApiConstans.DATA_TYPE_ARR,remark = "返回集合"),
            @APCOSApiResponse(name = "data.userAccount", value = "登录账户", dict = "登录账户",required = true,
                    fieldType = APCOSApiConstans.FIELD_TYPE_GN,
                    dataType = APCOSApiConstans.DATA_TYPE_STR,lenth = "50",remark = "登录账户"),
            @APCOSApiResponse(name = "data.id", value = "用户ID", dict = "用户ID",required = true,
                    fieldType = APCOSApiConstans.FIELD_TYPE_GN,
                    dataType = APCOSApiConstans.DATA_TYPE_STR,lenth = "50",remark = "用户ID"),
            @APCOSApiResponse(name = "data.name", value = "用户姓名", dict = "用户姓名",required = true,
                    fieldType = APCOSApiConstans.FIELD_TYPE_GN,
                    dataType = APCOSApiConstans.DATA_TYPE_STR,lenth = "50",remark = "用户姓名"),
            @APCOSApiResponse(name = "data.userPhone", value = "用户电话", dict = "用户电话",
                    fieldType = APCOSApiConstans.FIELD_TYPE_GN,
                    dataType = APCOSApiConstans.DATA_TYPE_STR,lenth = "50",remark = "用户电话"),
    })
    public List<OptionItem> getUsersByOrgId(String orgId) {
        InvokeCfg cfg = new InvokeCfg();
        cfg.setModuleKey(Constant.MODULE_KEY);
        cfg.setClassName(Constant.MODULE_CLASS_NAME);
        cfg.setFunName("getUsersByOrgId");
        HashMap<String, String> map = new HashMap<>();
        map.put("orgId",orgId);
        cfg.setArgs(map);
        JsonResult result = businessDelegate.invoke(cfg);
        if (result.getSuccess()) {
            if (result.getData() == null) {
                return null;
            }
            log.info("getUsersByOrgId:" + result.getData());
            JSONObject jsonObj = JSONObject.parseObject(result.getData().toString());
            return JSON.parseArray(jsonObj.get("data").toString(), OptionItem.class);
        }
//        else {
//            List<OptionItem> list = new ArrayList<>();
//            OptionItem item1 = new OptionItem("1","王麻子");
//            OptionItem item2 = new OptionItem("2","二狗子");
//            list.add(item1);
//            list.add(item2);
//            return list;
//        }
        return null;
    }

    public int saveOrUpdate(CdyardmgrPushInfo record){
        String id = record.getId();
        if(StringUtils.isEmpty(id)){
            record.setId(idWorker.nextId()+"");
            return cdacscontrolmgrPushInfoMapper.insert(record);
        }else {
            return cdacscontrolmgrPushInfoMapper.updateByKey(record);
        }
    }

    public int saveOrUpdateEvent(CdyardmgrEvent event){
        String id = event.getId();
        if(StringUtils.isEmpty(id)){
            event.setId(idWorker.nextId()+"");
            return cdacscontrolmgrEventMapper.insert(event);
        }else {
            return cdacscontrolmgrEventMapper.updateByKey(event);
        }
    }

    public String queryBarrierByVideoId(String videoId){
        HashMap param = new HashMap();
        param.put("videoId",videoId);
        CdparkmanagerProjectVideo projectVideo = cdacscontrolmgrProjectVideoMapper.selectOne(param);
        return projectVideo != null ? projectVideo.getVideoArea()+projectVideo.getBarrierNames() : "";
    }

    public CdparkmanagerProject getProjectInfoById(String id){
        return cdacscontrolmgrProjectMapper.selectByKey(id);
    }

    // 推送公众号消息
    @APCOSApiOperation(value = "pushPaMsg", description = "推送公众号消息", infDict = "推送公众号消息",
            reqDemo = "{\"templateId\":\"模板ID\",\"openId\":\"openId\",\"msgContent\":\"json格式的消息内容(根据模板定)\"}", respDemo = "{}",
            infType = APCOSApiConstans.INF_TYPE_OTHER)
    @APCOSApiRequests({
            @APCOSApiRequest(name = "templateId", value = "模板id", dict = "模板id",matchType = APCOSApiConstans.MATCH_TYPE_EQ,
                    fieldType = APCOSApiConstans.FIELD_TYPE_GN,
                    dataType = APCOSApiConstans.DATA_TYPE_STR,lenth = "50",remark = "模板id"),
            @APCOSApiRequest(name = "openId", value = "接收者openid", dict = "接收者openid",matchType = APCOSApiConstans.MATCH_TYPE_EQ,
                    fieldType = APCOSApiConstans.FIELD_TYPE_GN,
                    dataType = APCOSApiConstans.DATA_TYPE_STR,lenth = "50",remark = "接收者openid"),
            @APCOSApiRequest(name = "msgContent", value = "推送消息内容(json格式)", dict = "推送消息内容(json格式)",matchType = APCOSApiConstans.MATCH_TYPE_EQ,
                    fieldType = APCOSApiConstans.FIELD_TYPE_GN,
                    dataType = APCOSApiConstans.DATA_TYPE_STR,lenth = "2000",remark = "推送消息内容(json格式)"),
    })
    public void pushPaMsg(String templateId,String openId,String msgContent) {
        InvokeCfg cfg = new InvokeCfg();
        cfg.setModuleKey(Constant.MODULE_KEY);
        cfg.setClassName(Constant.MODULE_CLASS_NAME);
        cfg.setFunName("pushPaMsg");
        HashMap<String, String> map = new HashMap<>();
        map.put("templateId",templateId);
        map.put("openId",openId);
        map.put("msgContent",msgContent);
        cfg.setArgs(map);
        businessDelegate.invoke(cfg);
    }

    /**
     * 获取当前用户对象
     *
     * @return SysUser
     */
    @APCOSApiOperation(value = "getCurrentUser", description = "获取当前登录者", infDict = "获取当前登录者",
            reqDemo = "{}", respDemo = "{\"userId\":\"123\",\"userName\":\"张三\",\"userAccount\":\"13812345678\"}",
            infType = APCOSApiConstans.INF_TYPE_OTHER)
    @APCOSApiRequests({})
    @APCOSApiResponses({
            @APCOSApiResponse(name = "userAccount", value = "登录账户", dict = "登录账户",required = true,
                    fieldType = APCOSApiConstans.FIELD_TYPE_GN,
                    dataType = APCOSApiConstans.DATA_TYPE_STR,lenth = "50",remark = "登录账户"),
            @APCOSApiResponse(name = "userId", value = "用户ID", dict = "用户ID",required = true,
                    fieldType = APCOSApiConstans.FIELD_TYPE_GN,
                    dataType = APCOSApiConstans.DATA_TYPE_STR,lenth = "50",remark = "用户ID"),
            @APCOSApiResponse(name = "userName", value = "用户姓名", dict = "用户姓名",required = true,
                    fieldType = APCOSApiConstans.FIELD_TYPE_GN,
                    dataType = APCOSApiConstans.DATA_TYPE_STR,lenth = "50",remark = "用户姓名"),
    })
    public SysUserVO getCurrentUser() {
        JsonResult result = getJsonResult(Constant.MODULE_KEY,"getCurrentUser");
        if (result.getSuccess()) {
            if (result.getData() == null) {
                return null;
            }
            log.info("getCdsideparkmgrCurrentUser:" + result.getData());
            return JSON.parseObject(toJSONString(result.getData()), SysUserVO.class);
        }
//        else {
//            SysUserVO userVO = new SysUserVO();
//            userVO.setUserId("1");
//            userVO.setUserAccount("admin");
//            userVO.setUserName("admin");
//            return userVO;
//        }
        return null;
    }

    /**
     * 获取当前用户id获取所在组织
     *
     * @return SysUser
     */
    public SysOrgInfo getCurrentOrg() {
        SysUserVO currentUser = getCurrentUser();
        if(currentUser == null){return null; }
        return getOrgByUserId(currentUser.getUserId());
    }

    private JsonResult getJsonResult(String keyName,String funName) {
        InvokeCfg cfg = new InvokeCfg();
        cfg.setModuleKey(keyName);
        cfg.setClassName(Constant.MODULE_CLASS_NAME);
        cfg.setFunName(funName);
        return businessDelegate.invoke(cfg);
    }

    /**
     * 查询角色列表
     * @return
     */
    @APCOSApiOperation(value = "loadRoleList", description = "获取组织下的角色列表", infDict = "获取组织下的角色列表",
            reqDemo = "{\"orgId\":\"123\"}", respDemo = "{\n" +
            "  \"data\": [\n" +
            "    {\n" +
            "      \"id\": \"角色ID\",\n" +
            "      \"name\": \"角色名称\"\n" +
            "    }\n" +
            "  ]\n" +
            "} ",
            infType = APCOSApiConstans.INF_TYPE_OTHER)
    @APCOSApiRequests({
            @APCOSApiRequest(name = "orgId", value = "组织ID", dict = "组织ID",matchType = APCOSApiConstans.MATCH_TYPE_EQ,
                    fieldType = APCOSApiConstans.FIELD_TYPE_GN,required = true,
                    dataType = APCOSApiConstans.DATA_TYPE_STR,lenth = "50",remark = "组织ID"),
    })
    @APCOSApiResponses({
            @APCOSApiResponse(name = "data", value = "返回集合", dict = "返回集合",required = true,
                    fieldType = APCOSApiConstans.FIELD_TYPE_GN,keyType = APCOSApiConstans.KEY_TYPE_LIST,
                    dataType = APCOSApiConstans.DATA_TYPE_ARR,remark = "返回集合"),
            @APCOSApiResponse(name = "data.id", value = "角色ID", dict = "角色ID",required = true,
                    fieldType = APCOSApiConstans.FIELD_TYPE_GN,
                    dataType = APCOSApiConstans.DATA_TYPE_STR,lenth = "50",remark = "角色ID"),
            @APCOSApiResponse(name = "data.name", value = "角色名称", dict = "角色名称",required = true,
                    fieldType = APCOSApiConstans.FIELD_TYPE_GN,
                    dataType = APCOSApiConstans.DATA_TYPE_STR,lenth = "50",remark = "角色名称"),
    })
    public JsonResult loadRoleList() {
        SysUserVO currentUser = getCurrentUser();
        if(currentUser == null){
            return null;
        }
        InvokeCfg cfg = new InvokeCfg();
        cfg.setModuleKey(Constant.MODULE_KEY);
        cfg.setClassName(Constant.MODULE_CLASS_NAME);
        cfg.setFunName("loadRoleList");
        HashMap<String, String> map = new HashMap<>();
        map.put("orgId",currentUser.getOrgId());
        cfg.setArgs(map);
        JsonResult result = businessDelegate.invoke(cfg);
        log.info("loadRoleList =====>" + result.toString());
        if(result.getSuccess()){
            return result;
        }
//        else {
//            List<OptionItem> list = new ArrayList<>();
//            OptionItem item1 = new OptionItem("1","角色1");
//            OptionItem item2 = new OptionItem("2","角色2");
//            list.add(item1);
//            list.add(item2);
//            return JsonResult.ok(list);
//        }
        return null;
    }

    /**
     * 查询用户列表
     * @return
     */
    public JsonResult loadUserList() {
        SysOrgInfo currentOrg = getCurrentOrg();
        if(currentOrg == null){
            return null;
        }
        List<OptionItem> users = getUsersByOrgId(currentOrg.getOrgId());
        return JsonResult.ok(users);
    }

    /**
     * 字典类型获取字典项
     * @param dictType 字典类型
     * @return
     */
//    @APCOSApiOperation(value = "getDictItems", description = "根据字典类型查询字典项", infDict = "根据字典类型查询字典项",
//            reqDemo = "{\"dictType\":\"业务类型\"}", respDemo = " {}",
//            infType = APCOSApiConstans.INF_TYPE_OTHER)
//    @APCOSApiRequests({
//            @APCOSApiRequest(name = "dictType", value = "字典类型", dict = "字典类型",matchType = APCOSApiConstans.MATCH_TYPE_EQ,
//                    keyType = APCOSApiConstans.KEY_TYPE_STR,fieldType = APCOSApiConstans.FIELD_TYPE_GN,required = true,
//                    dataType = APCOSApiConstans.DATA_TYPE_STR,lenth = "50",remark = "字典类型"),
//    })
//    public JsonResult getDictItems(String dictType) {
//        InvokeCfg cfg = new InvokeCfg();
//        cfg.setModuleKey(Constant.MODULE_KEY);
//        cfg.setClassName(Constant.MODULE_CLASS_NAME);
//        cfg.setFunName("getDictItems");
//        HashMap<String, String> map = new HashMap<>();
//        map.put("dictType", dictType);
//        cfg.setArgs(map);
//        JsonResult result = businessDelegate.invoke(cfg);
//        log.info("getDictItems =====>" + result.toString());
//        if(result.getSuccess()){
//            return result;
//        }
//        else {
//            List<DictItem> list = new ArrayList<>();
//            DictItem item1 = new DictItem("1","工程车辆");
//            DictItem item2 = new DictItem("2","社会车辆");
//            list.add(item1);
//            list.add(item2);
//            return JsonResult.ok(list);
//        }
//        return null;
//    }

    @APCOSApiOperation(value = "controlDevice", description = "控制设备", infDict = "控制设备",
            reqDemo = "{\"controlParams\":\"%7B%22p1%22%3A%22on%22%7D\",\"serialNum\":\"000d6f000a8f5379\",\"operation\":\"on-off\"}", respDemo = "{}",
            infType = APCOSApiConstans.INF_TYPE_OTHER)
    @APCOSApiRequests({
            @APCOSApiRequest(name = "controlParams", value = "控制参数", dict = "控制参数",matchType = APCOSApiConstans.MATCH_TYPE_EQ,
                    fieldType = APCOSApiConstans.FIELD_TYPE_GN,required = true,
                    dataType = APCOSApiConstans.DATA_TYPE_STR,lenth = "2000",remark = "控制参数"),
            @APCOSApiRequest(name = "operation", value = "操作", dict = "操作",matchType = APCOSApiConstans.MATCH_TYPE_EQ,
                    fieldType = APCOSApiConstans.FIELD_TYPE_GN,required = true,
                    dataType = APCOSApiConstans.DATA_TYPE_STR,lenth = "50",remark = "操作"),
            @APCOSApiRequest(name = "serialNum", value = "设备序列号", dict = "设备序列号",matchType = APCOSApiConstans.MATCH_TYPE_EQ,
                    fieldType = APCOSApiConstans.FIELD_TYPE_GN,required = true,
                    dataType = APCOSApiConstans.DATA_TYPE_STR,lenth = "50",remark = "设备序列号"),
    })
    private JsonResult controlDevice(String operation, String mac,String controlParams) {
        InvokeCfg cfg = new InvokeCfg();
        cfg.setModuleKey(Constant.MODULE_KEY);
        cfg.setClassName(Constant.MODULE_CLASS_NAME);
        cfg.setFunName("controlDevice");
        Map<String, Object> map = new HashMap<>();
        map.put("operation", operation);
        map.put("serialNum", mac);
        map.put("controlParams", controlParams);
        cfg.setArgs(map);
        log.info("财门门禁管理模块控制设备的参数: " + map.toString());
        return businessDelegate.invoke(cfg);
    }

    /**
     *
     * @param personId 人员ID 0001长度不超过19字节
     * @param userName 业主或访客姓名 XiaoMing长度不超过63字节
     * @param userRole 业主或访客角色 1：白名单 2：黑名单
     * @param userCategory  业主或访客分类 取值范围：0~15。0为不分类，其它用于分时调度
     * @param base64Pic  注册图像(base64以/9j开头)
     * @param startTime  2017/11/10 12:00:00     # 新增时不传默认为useless
     * @param endTime  2017/12/18 16:45:30    # 新增时不传默认为forever;forever表示永久有效，never表示永久无效
     * @param customerText  用户自定义文本内容,人员对比成功后的语音播报或屏幕显示可以将此字段作为输出的一部分
     * @param mac  设备mac
     * @return
     */
    //@Async("cdacsAsyncServiceExecutor")
    public JsonResult uploadPortraitData(String personId,String userName,String userRole,
                                     String userCategory, String base64Pic,String startTime,String endTime,
                                     String customerText,String mac) {
        if(StringUtils.isEmpty(personId)){
            return JsonResult.error("人员ID不存在");
        }
        if(StringUtils.isEmpty(userName)){
            return JsonResult.error("业主或访客姓名不存在");
        }
        if(StringUtils.isEmpty(userRole)){
            return JsonResult.error("业主或访客角色不存在");
        }
        if(StringUtils.isEmpty(userCategory)){
            return JsonResult.error("业主或访客分类不存在");
        }
        JSONObject pJsonObj = new JSONObject();
        pJsonObj.put("p1",personId);
        pJsonObj.put("p2", userName);
        pJsonObj.put("p3", userRole);
        pJsonObj.put("p4", userCategory);
        pJsonObj.put("p5", base64Pic.replaceFirst(Constant.IMG_BASE64_PREFIX,""));
        pJsonObj.put("p6", startTime);
        pJsonObj.put("p7", endTime);
        pJsonObj.put("p8", customerText);
        // String controlParams = pJsonObj.toJSONString();
        String controlParams = URLEncoder.encodeURIComponent(pJsonObj.toJSONString());
        JsonResult result = controlDevice("Upload_portrait_data",mac, controlParams);
        if(result.getSuccess()){
            log.info("门禁项目管理模块控制上传人像数据返回："+result.toString());
            String resultStr = result.getData().toString();
            boolean succeed = isSucceed(resultStr, Constant.DEVICE_RESPONSE_R1, Constant.ZERO);
            if (succeed) {
                return JsonResult.ok("上传人像数据成功",result.getData());
            }else {
                return JsonResult.error("[mac: {"+mac+"}]上传人像数据失败,原因是: " + getErrMsg2(resultStr));
            }
        }
        return JsonResult.error("[mac: {"+mac+"}]上传人像数据失败,请重试或联系管理员");
    }

    /**
     *
     * @param personId 人员ID
     * @return
     */
    public JsonResult deletePortraitData(String personId,String projectId) {
        if(StringUtils.isEmpty(personId)){
            return JsonResult.error("人员ID不存在");
        }
        HashMap param = new HashMap();
        param.put("projectId",projectId);
        List<CdparkmanagerProjectAccess> list = projectAccessMapper.list(param);
        int count = 0;
        String errorMsg = "";
        for(CdparkmanagerProjectAccess item : list){
            JSONObject pJsonObj = new JSONObject();
            // 标记[{"key":"ID删除 ","value":"-1"},
            // {"key":"删除所有普通人员","value":"0"},{"key":"删除所有白名单人员","value":"1"},
            // {"key":"删除所有黑名单人员","value":"2"},{"key":"删除所有人员记录","value":"3"}]
            pJsonObj.put("p1","-1"); // ID删除
            pJsonObj.put("p2", personId);
            String controlParams = pJsonObj.toJSONString();
            JsonResult result = controlDevice("Delete_portrait_data",item.getFaceMac(), controlParams);
//            JsonResult result = new JsonResult();
//            result.setSuccess(true);
//            result.setData("{\"r1\":\"0\"}");
            if(result.getSuccess()){
                log.info("通行管理模块控制删除人像数据返回："+result.toString());
                String resultStr = result.getData().toString();
                boolean succeed = isSucceed(resultStr, Constant.DEVICE_RESPONSE_CODE, Constant.ZERO);
                if (succeed) {
                    count++;
                }else {
                    errorMsg = getErrMsg(resultStr);
                }
            }
        }
        if(count > 0){
            return JsonResult.ok("删除人像数据成功",null);
        }
        return JsonResult.error("删除人像数据失败,原因是: " + errorMsg);
    }

    // 来访登记
    public JsonResult saveVisitorRegist(CdacscontrolmgrRegistInfo record) {
        if(record == null || StringUtils.isEmpty(record.getProjectId())){
            return JsonResult.error("参数错误");
        }
        String projectId = record.getProjectId();
        String fileFullUrl = record.getFile();
        if(!StringUtils.isEmpty(fileFullUrl)){
            try {
                String fileType = fileFullUrl.substring(fileFullUrl.lastIndexOf(".")+1);
                String filePath = ImageUtils.downLoadFromUrl(fileFullUrl, this.filePath);
                if(StringUtils.isEmpty(filePath)){
                    return JsonResult.error("图片下载失败");
                }
                File file = new File(filePath);
                String prefix = ImageUtils.getPreTypeByImgType(fileType);
                String faceBytes;
                if(file.length() > 200*1024){
                    byte[] bytes = ImageUtils.commpressPicCycle(ImageUtils.getBytes(filePath), 200, 0.5f);
                    BASE64Encoder encoder = new BASE64Encoder();
                    faceBytes = encoder.encode(bytes).replaceAll("\r|\n", "");
                }else {
                    faceBytes = ImageUtils.getBase64FromFile(filePath);
                }
                record.setBase64Pic(prefix + faceBytes);
            }catch (Exception e){
                log.error("压缩转换图片失败，" + e.toString());
                return JsonResult.error("压缩图片失败");
            }
        }
        String visitDate = record.getVisitDate();
        String comeinTime = record.getComeinTime();
        String leaveTime = record.getLeaveTime();
        String space = Constant.SPACE;
        if(StringUtils.isEmpty(visitDate) || StringUtils.isEmpty(comeinTime) || StringUtils.isEmpty(leaveTime)){
            return JsonResult.error("到访时间不能为空");
        }
        String scheduleTime = visitDate + space + leaveTime;
        visitDate = visitDate.replace("-","/");
        String startTime = visitDate + space + comeinTime;
        String endTime = visitDate + space + leaveTime;
        HashMap param = new HashMap();
        param.put("projectId", projectId);
        List<CdparkmanagerProjectAccess> list = projectAccessMapper.list(param);
        int count = 0;
        boolean flag = false;
        String errorMsg = "";
        String tempPwd = "";
        for(CdparkmanagerProjectAccess item : list){
            String id = String.valueOf(idWorker.nextId());
            String faceMac = item.getFaceMac();
            String pwdMac = item.getPasswordMac();
            if(!StringUtils.isEmpty(faceMac)){
                JsonResult jsonResult = uploadPortraitData(id, record.getVisitorName(), "1", "0", record.getBase64Pic(), startTime, endTime, id, faceMac);
    //            JsonResult jsonResult = new JsonResult();
    //            jsonResult.setSuccess(true);
    //            jsonResult.setData("{\"r1\":\"0\"}");
                // JsonResult [status=200, success=true, msg= 操作成功！, data={"r2":"1468392773227933696","code":"0","r1":"40"}]
                if(jsonResult.getSuccess()){
                    String dataStr = jsonResult.getData().toString();
                    JSONObject jsonObject = JSONObject.parseObject(dataStr);
                    String r1Str = jsonObject.getString("r1");
                    if(Constant.ZERO.equals(r1Str)){ //0表示成功
                        record.setId(id);
                        record.setStatus(Constant.REGIST_INFO_STATUS_NEW);
                        if(!flag){
                            if(!StringUtils.isEmpty(pwdMac)){
                                tempPwd = Utils.getRandom8();
                                record.setTempPwd(AESUtil.encrypt(tempPwd,Constant.ENCRYPT_KEY));
                            }
                            int num = registInfoMapper.insert(record);
                            if(num > 0){
                                // 过了到访时间更新状态和删除人脸
                                Date date = DateUtil.string2Date(scheduleTime);
                                log.info("删除人脸的时间是: " + date);
                                if(date != null){
                                    Timer timer = new Timer();
                                    timer.schedule(new PortraitDeleteTask(this,registInfoMapper,id,projectId),date);
                                }
                                flag = true;
                                if(!StringUtils.isEmpty(pwdMac)){
                                    addSchedulerJob(visitDate,comeinTime,id);
                                }
                            }
                        }
                        count++;
                    }else {
                        errorMsg = getErrMsg2(dataStr);
                    }
                }
            }else {
                if(!flag && !StringUtils.isEmpty(pwdMac)){
                    record.setId(id);
                    record.setStatus(Constant.REGIST_INFO_STATUS_NEW);
                    tempPwd = Utils.getRandom8();
                    record.setTempPwd(AESUtil.encrypt(tempPwd,Constant.ENCRYPT_KEY));
                    int num = registInfoMapper.insert(record);
                    if(num > 0){
                        addSchedulerJob(visitDate,comeinTime,id);
                        flag = true;
                    }
                    count++;
                }
            }
        }
        if(count > 0){
            return JsonResult.ok("登记成功",tempPwd);
        }
        return JsonResult.error("登记失败,原因可能是: " + errorMsg);
    }

    private void addSchedulerJob(String visitDate,String comeinTime,String id){
        String[] dateArr = visitDate.split("/");
        int year = Integer.parseInt(dateArr[0]);
        String yearStr = year + "-" + (year+1);
        String month = Integer.parseInt(dateArr[1])+"";
        String day = Integer.parseInt(dateArr[2])+"";
        String[] timeArr = comeinTime.split(":");
        String hour = Integer.parseInt(timeArr[0])+"";
        String minutes = Integer.parseInt(timeArr[1])+"";
        // 注意这里的年份是个区间
        String cron = CronUtil.getCron(yearStr,"?",month,day,hour,minutes,"0");
        try {
            scheduler.addJob(id, Constant.QUZRTZ_GROUP_PREFIX+id,cron);
        } catch (SchedulerException e) {
            log.error(e.toString());
        }
    }

    /**
     * 控制 IO 输出
     * @param delayTime 先通后断的延迟时间
     * @param ioCode 对应的输出 IO编号 必需值，正常情况下只有 0，1 两个输出编号
     * @param statusValue 输出 IO 的状态值 [{"key":"断","value":"0"},{"key":"通","value":"1"},{"key":" 先通后断","value":"2"}]
     * @param mac  设备mac
     * @return
     */
    public JsonResult ioctlResp(String delayTime,String ioCode, String statusValue,String mac) {
        if(StringUtils.isEmpty(delayTime)){
            return JsonResult.error("先通后断的延迟时间不存在");
        }
        if(StringUtils.isEmpty(ioCode)){
            return JsonResult.error("对应的输出IO编号不存在");
        }
        if(StringUtils.isEmpty(statusValue)){
            return JsonResult.error("输出IO的状态值不存在");
        }
        JSONObject pJsonObj = new JSONObject();
        pJsonObj.put("p1",delayTime);
        pJsonObj.put("p2", ioCode);
        pJsonObj.put("p3", statusValue);// 通
        String controlParams = pJsonObj.toJSONString();
        JsonResult result = controlDevice("ioctl_resp",mac, controlParams);
        log.info("门禁项目管理模块控制控制IO输出(抬竿)返回："+result.toString());
        String resultStr = result.getData().toString();
        boolean succeed = isSucceed(resultStr, Constant.DEVICE_RESPONSE_CODE, Constant.ZERO);
        if (succeed) {
            return JsonResult.ok("控制控制IO输出(抬竿)成功",result.getData());
        }else {
            return JsonResult.error("控制控制IO输出(抬竿)失败,原因是:"+getErrMsg(resultStr));
        }
    }

    private boolean isSucceed(String result){
        if(StringUtils.isEmpty(result)){return false;}
        JSONObject returnJson = JSONObject.parseObject(result);
        if(returnJson == null){
            return false;
        }
        String responseCode = returnJson.getString("code");
        return Constant.ZERO.equals(responseCode);
    }

    private boolean isSucceed(String result,String key,String keyResult){
        if(StringUtils.isEmpty(result)){return false;}
        JSONObject returnJson = JSONObject.parseObject(result);
        if(returnJson == null){
            return false;
        }
        String responseCode = returnJson.getString(key);
        return keyResult.equals(responseCode);
    }

    private String getErrMsg(String result){
        if(StringUtils.isEmpty(result)){return "";}
        JSONObject returnJson = JSONObject.parseObject(result);
        if(returnJson == null){
            return "[未知]";
        }
        return returnJson.getString("message");
    }
    private String getErrMsg2(String result){
        if(StringUtils.isEmpty(result)){return "";}
        JSONObject returnJson = JSONObject.parseObject(result);
        if(returnJson == null){
            return "[未知]";
        }
        return ErrorCodeEnum.getDesc(returnJson.getString("r1"));
    }

    /**
     * GPIO控制
     * @param mac  设备mac
     * @return
     */
    public JsonResult gpioControl(String mac) {
        if(StringUtils.isEmpty(mac)){
            return JsonResult.error("设备mac为空");
        }
        JSONObject pJsonObj = new JSONObject();
        //控制模式[{"key":"测试模式","value":"test"},{"key":"强制模式","value":"force"},{"key":"场景模式","value":"scene"}]
        pJsonObj.put("p1","force");
        HashMap param = new HashMap();
        param.put("faceMac", mac);
        CdparkmanagerProjectAccess projectAccess = projectAccessMapper.selectOne(param);
        if(projectAccess == null){
            return JsonResult.error("该Mac未绑定到对应的门禁项目");
        }
        param.put("projectId",projectAccess.getProjectId());
        CdacscontrolmgrOwner owner = cdacscontrolmgrOwnerMapper.selectOne(param);
        String userId = owner != null ? owner.getId() : "";
        pJsonObj.put("p2", userId);//人员id 查询mac对应门禁项目下面的人员id
        String controlParams = pJsonObj.toJSONString();
        JsonResult result = controlDevice("GPIO_controller",mac, controlParams);
        log.info("门禁项目管理模块GPIO控制返回："+result.toString());
        String resultStr = result.getData().toString();
        if (isSucceed(resultStr,Constant.DEVICE_RESPONSE_CODE,Constant.ZERO)) {
            return JsonResult.ok("门禁项目管理模块GPIO控制成功",result.getData());
        }else {
            return JsonResult.error("门禁项目管理模块GPIO控制失败,原因是:"+getErrMsg(resultStr));
        }
    }

    /**
     * 开门
     * @param mac 设备mac
     * @return
     */
    public JsonResult openDoor(String mac) {
        if(StringUtils.isEmpty(mac)){
            return JsonResult.error("mac不存在");
        }
        JSONObject pJsonObj = new JSONObject();
        pJsonObj.put("p1", Constant.CMD_ADDR);//控制器地址
        pJsonObj.put("p2", Constant.DOOR_CODE);//门地址
        String controlParams = pJsonObj.toJSONString();
        JsonResult result = controlDevice("Open_door",mac, controlParams);
        log.info("门禁项目管理模块控制开门返回："+result.toString());
        String resultStr = result.getData().toString();
        if (isSucceed(resultStr,Constant.DEVICE_RESPONSE_CODE,Constant.ZERO)) {
            return JsonResult.ok("控制开门成功",result.getData());
        }else {
            return JsonResult.error("控制开门失败,原因是:"+getErrMsg(resultStr));
        }
    }

    /**
     * 应答刷卡记录
     * @param isOpen 是否开门 01
     * @param mac 设备mac
     * @return
     */
    public JsonResult responseCardSwipingRecord(boolean isOpen,String mac,String event) {
        if(StringUtils.isEmpty(mac)){
            return JsonResult.error("mac不存在");
        }
        JSONObject pJsonObj = new JSONObject();
        pJsonObj.put("p1", Constant.CMD_ADDR);//控制器地址
        pJsonObj.put("p2", Constant.DOOR_CODE);//门编号
        pJsonObj.put("p3", isOpen ? "01" : Constant.KEEP_VALUE);//是否开门 是否通过验证，通过则开锁。01开门
        pJsonObj.put("p4", Constant.BYTE_4_HEX_NULL);//开门时间 2字节输出继电器动作的时间，秒为单位。
        pJsonObj.put("p5", Constant.BYTE_18_HEX_NULL);//卡号 18字节 用于显示卡号字符串，为0表示无。
        pJsonObj.put("p6", Constant.BYTE_16_HEX_NULL);//姓名 GB2312编码16字节 。  用于显示的姓名字符串，为0表示无。
        pJsonObj.put("p7", event);//事件 GB2312编码32字节，用于显示的事件字符串，为0表示无。
        pJsonObj.put("p8", Constant.BYTE_20_HEX_NULL);//时间 20字节    用于显示的时间字符串，为0表示无。
        String controlParams = pJsonObj.toJSONString();
        JsonResult result = controlDevice("Response_card_swiping_record",mac, controlParams);
        log.info("门禁项目管理模块应答刷卡记录返回："+result.toString());
        String resultStr = result.getData().toString();
        if (isSucceed(resultStr,Constant.DEVICE_RESPONSE_R1,Constant.ACS_RESPONSE_SUCCESS)) {
            return JsonResult.ok("应答刷卡记录成功",result.getData());
        }else {
            return JsonResult.error("应答刷卡记录失败,原因是:"+getErrMsg(resultStr));
        }
    }
}
